﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using SKYPE4COMLib;
using System.Threading;
using System.IO;
using System.Windows.Forms;

using System.Windows.Threading;
using System.ComponentModel;
using System.Diagnostics;

namespace SoundServant
{
    public class Sharing
    {
        #region Variables

        Congregation congregation;
        Recorder recorder;
        bool autoDialOnRecorderStart = true;
        DateTime callTimeStarted;
        TimeSpan finishedCallLength;
        short calls = 0;
        ArrayList listeners = new ArrayList();
        ArrayList addToConference = new ArrayList();
        bool attached, initialized, initializing, attaching, startWhenReady = false;
        public Skype skype;
        Thread initializer;

        #endregion

        #region Properties

        public ArrayList Listeners { get { return listeners; } }
        public CallCollection Calls { get { return skype.ActiveCalls; } }
        public bool AutoDial { get { return autoDialOnRecorderStart; } set { autoDialOnRecorderStart = value; } }
        public TimeSpan CallTime
        {
            get
            {
                if (calls > 0)
                {
                    return DateTime.Now - callTimeStarted;
                }
                else if (finishedCallLength != null)
                {
                    return finishedCallLength;
                }
                else
                {
                    return new TimeSpan(0, 0, 0);
                }
            }
        }
        public float Balance
        {
            get
            {
                if (attached)
                {
                    return float.Parse(skype.get_Profile("PSTN_BALANCE").ToString()) / 100f;
                }
                else return 0f;
            }
        }
        public string CurrentUserHandle
        {
            get
            {
                if (attached)
                {
                    return skype.CurrentUserHandle;
                }
                return "not logged in";
            }

        }
        public string BalanceString
        {
            get
            {
                return skype.CurrentUserProfile.BalanceToText;
            }
        }

        #endregion

        #region Events

        public delegate void VoidEventHandler();
        public event VoidEventHandler ListenersUpdated;
        public event VoidEventHandler Initialized;
        public event VoidEventHandler CallsUpdated;
        public event VoidEventHandler Detached;
        public event VoidEventHandler Attached;
        public event VoidEventHandler CallsStarted;
        public event VoidEventHandler CallsFinished;
        public event VoidEventHandler Initializing;
        public delegate void ListenerEventHandler(Listener listener);
        public event ListenerEventHandler ListenerAdded;
        public event ListenerEventHandler ListenerDeleted;

        public delegate void AttachmentStatusEventHandler(TAttachmentStatus attachmentStatus);
        public event AttachmentStatusEventHandler AttachmentStatus;
        public delegate void MessageEventHandler(Command command);
        public event MessageEventHandler Message;

        #endregion

        public Sharing(Congregation _cong, Recorder _recorder)
        {
            Thread.CurrentThread.SetApartmentState(ApartmentState.STA);

            congregation = _cong;
            recorder = _recorder;


            listeners = Listener.GetAllListeners(congregation);
            foreach (Listener listener in listeners)
            {
                listener.ConnectRequest += new Listener.ListenerEventHandler(AddListenerToConference);
            }

            if (ListenersUpdated != null) ListenersUpdated();

            skype = new Skype();
            ((_ISkypeEvents_Event)skype).AttachmentStatus += AttachmentStatusMessage;
            ((_ISkypeEvents_Event)skype).Reply += CommandReply;
            skype.CallStatus += new _ISkypeEvents_CallStatusEventHandler(SkypeCallStatusUpdate);
            recorder.RecorderStarted += new Recorder.VoidEventHandler(RecorderStarted);
            recorder.RecorderFinished += new Recorder.VoidEventHandler(RecorderFinished);
            skype.CallSeenStatusChanged += new _ISkypeEvents_CallSeenStatusChangedEventHandler(skype_CallSeenStatusChanged);
            skype.CallDtmfReceived += new _ISkypeEvents_CallDtmfReceivedEventHandler(skype_CallDtmfReceived);
            skype.VoicemailStatus += new _ISkypeEvents_VoicemailStatusEventHandler(skype_VoicemailStatus);

        }

        void skype_VoicemailStatus(Voicemail pMail, TVoicemailStatus Status)
        {
            Logs.Information("Skype", "Killing Voicemail - Listener: " + pMail.PartnerHandle + " Status: " + pMail.Status.ToString());
            pMail.Delete();
        }

        void skype_CallDtmfReceived(Call pCall, string code)
        {
            Logs.Information("Dtmf Status Received", pCall.Duration.ToString());
        }

        void skype_CallSeenStatusChanged(Call pCall, bool Status)
        {
            Logs.Information("Sharing Call Status", pCall.Duration.ToString());
        }

        bool AddListenerToConference(Listener listener)
        {
            // Join listener to conference
            Logs.Information("Skype", "Processing connect request from listener - Name: " + listener.Name + " Handle: " + listener.Contact);

            if (!attached || skype.ActiveCalls.Count == 0)
            {
                return true;
            }

            else if(attached)
            {
                ConnectingWindow.RunConferenceJoiner(skype, listener);
                skype.SilentMode = true;
            }
            return false;
        }

        public void Initialize()
        {
            // Check Sharing Status
            if (initialized == true || attached == true)
            {
                Detatch();
            }
            initializing = true;
            if (Initializing != null) Initializing();

            // Shut Skype Client Down
            if (skype.Client.IsRunning) skype.Client.Shutdown();

            // Skype Killer
            Thread.Sleep(1000);
            int n = 0;

            while (((Process[])Process.GetProcessesByName("Skype")).Length > 1)
            {
                if (n > 10)
                {
                    Logs.Information("Skype", "Killing Skype Process - Attempt: " + n);
                    ((Process[])Process.GetProcessesByName("Skype"))[0].Kill();
                    System.Windows.Forms.Application.DoEvents();
                }
                Thread.Sleep(100);
                n++;
            }

            Logs.Information("Skype", "The skype client was succesfully shutdown");

            // Check for skype config files and copy if they Exist
            System.Windows.Forms.Application.DoEvents();
            if (congregation.SkypeConfigDir.Exists)
            {
                DirectoryInfo applicationData = new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData));
                DirectoryInfo skypeApplicationData = new DirectoryInfo(applicationData.FullName + "\\Skype");

                Logs.Information("Skype", "Writing congregation Skype Configuration Files - From: " + congregation.SkypeConfigDir.FullName + " To: " + applicationData);
                SS.CopyDirectory(congregation.SkypeConfigDir, skypeApplicationData);
                Logs.Information("Skype", "Skype Configuration Files Writtern");

                Logs.Information("Skype", "Starting skype client");
                skype.Client.Start(true, true);
                initialized = true;
                if (Initialized != null) Initialized();

                skype.Attach(9, false);
            }
            else
            {
                SSMessageBox.Show("Skype Snapshot not found", "No Skype Snapshot exists for this congregation. You must create one before using Sound Servant to share the meeting via Skype.", SSMessageBoxType.ExclamationOk);
                Logs.Information("Skype", "No skype configuration files were found for this congregation");
            }
        }
        public void Detatch()
        {
            Logs.Information("Skype", "Detaching from Skype");
            if (Options.Default.SnapshotSkype)
            {
                SaveSkypeConfigurationWindow.Execute(congregation, skype);
            }
            else
            {
                skype.Client.Shutdown();
            }
            initialized = false;
            attached = false;
            if (Detached != null) Detached();
        }

        private void RecorderStarted()
        {
            if (autoDialOnRecorderStart && listeners.Count > 0 && (recorder.Recording.Type == MeetingType.COVisit || recorder.Recording.Type == MeetingType.SchoolServiceMeeting || recorder.Recording.Type == MeetingType.TalkWatchtowerStudy))
            {
                if (attached)
                {
                    Connect();
                }
                else
                {
                    startWhenReady = true;
                }
            }
        }
        void RecorderFinished()
        {
            if (autoDialOnRecorderStart)
            {
                Disconnect();
            }
        }

        public void AddListener(Listener listener)
        {
            Logs.Information("Sharing", "Adding Listener - Id: " + listener.Id + " Name: " + listener.Name);

            listeners.Add(listener);
            listener.ConnectRequest += new Listener.ListenerEventHandler(AddListenerToConference);

            if (ListenerAdded != null) ListenerAdded(listener);

            if (listener.ParticipateByDefault)
            {
                listener.Connect();
            }
        }
        public void DeleteListener(Listener listener)
        {
            listener.Disconnect();
            listener.Delete();

            listeners.Remove(listener);
            Logs.Information("Sharing", "Deleting Listener - Id: " + listener.Id + " Name: " + listener.Name);

            ListenerDeleted(listener);
        }

        #region Skype Commands

        public void ConnectDisconnect()
        {
            if (attached)
            {
                if (skype.ActiveCalls.Count > 0)
                {
                    Disconnect();
                }
                else
                {
                    Connect();
                }
            }
            else
            {
                SSMessageBox.Show("Cannot place call, you are not attached to Skype");
            }
        }
        void Connect()
        {
            skype.SilentMode = true;
            
            // Check for Active Calls
            if (skype.ActiveCalls.Count == 0 && listeners.Count > 0)
            {
                // Iterate through Listeners Array and add 'Ready' listeners to dial string
                int n = 0;
                string dialString = "";
                foreach (Listener listener in listeners)
                {
                    if (listener.State == ListenerState.Ready)
                    {
                        dialString += listener.Contact + ", ";
                        n++;
                    }
                }

                // If Listeners have been added to dial string, play call
                if (n > 0)
                {
                    dialString = dialString.Substring(0, dialString.Length - 2);

                    try
                    {
                        Logs.Information("Skype", "Placing Call - Dial String: " + dialString);
                        skype.PlaceCall(dialString, "", "", "");
                    }
                    catch (Exception e)
                    {
                        Logs.Error("Skype", "Error placing Call", e.ToString());
                    }
                }
            }
        }
        public void Disconnect()
        {
            if (attached && skype.ActiveCalls.Count > 0)
            {
                Logs.Information("Skype", "Dropping all Listeners");
                foreach (Call call in skype.ActiveCalls)
                {
                    Logs.Information("Skype", "Dropping Listener - Handler: " + call.PartnerHandle + " Call Id: " + call.Id);
                    call.Finish();
                }
            }
        }
        public void SendCommand(string commandText)
        {
            Command command = new Command();
            command.Command = commandText;
            command.Blocking = false;
            skype.SendCommand(command);
        }
        public void OpenSkypeRequest()
        {
            if (!skype.Client.IsRunning)
                skype.Client.Start(false, false);
        }
        public void AttachRequest()
        {
            if (skype.Client.IsRunning)
            {
                skype.Attach(9, false);
                
            }
        }

        #endregion

        #region SkypeFeedback

        void SkypeCallStatusUpdate(Call pCall, TCallStatus Status)
        {
            foreach (Listener listener in listeners)
            {
                if (listener.Contact == pCall.PartnerHandle)
                {
                    listener.UpdateCallStatus(pCall, Status);
                    Logs.Information("Skype", "Call associated with Listener - Id: + " + pCall.Id + " Listener: " + listener.Name + "  Status: " + Sharing.ResolveCallStatus(pCall.Status) + " Handle: + " + pCall.PartnerHandle + " Duration: " + pCall.Duration);
                    break;
                }
            }

            if (calls == 0 && skype.ActiveCalls.Count > 0)
            {
                Logs.Information("Skype", "In Call");
                callTimeStarted = DateTime.Now;
                if (CallsStarted != null) CallsStarted();
            }

            if (calls > (short)0 && skype.ActiveCalls.Count == 0)
            {
                Logs.Information("Skype", "All calls finished");
                finishedCallLength = (TimeSpan)(DateTime.Now - callTimeStarted);
                if (CallsFinished != null) CallsFinished();
            }

            calls = (short)skype.ActiveCalls.Count;

            if (CallsUpdated != null) CallsUpdated();
            
            // Fix to prevent skype windows from popping up when a new call is connected
            //Thread.Sleep(2000);
            //skype.SilentMode = true;
            //skype.Client.Minimize();
        }
        void AttachmentStatusMessage(TAttachmentStatus Status)
        {
            Logs.Information("Skype", "Attachment Status: " + skype.Convert.AttachmentStatusToText(Status));
            if (Status == TAttachmentStatus.apiAttachAvailable)
            {
                attached = false;
                attaching = true;
                Logs.Information("Skype", "Sending Skype Attachment Request");
                skype.Attach(9, false);
            }
            if (Status == TAttachmentStatus.apiAttachSuccess)
            {
                attaching = false;
                attached = true;
                Attached();
                skype.SilentMode = true;
                skype.Client.Minimize();
                if (startWhenReady) ConnectDisconnect();
            }
            else if (attached && Status != TAttachmentStatus.apiAttachSuccess)
            {
                attached = false;
                attaching = true;
                if (Detached != null) Detached();
            }

            if (AttachmentStatus != null) AttachmentStatus(Status);
        }
        public void CommandReply(Command command)
        {
            string[] replySegs = command.Reply.Split(' ');
            if (Message != null && ((replySegs.Length > 1 && replySegs[2] != "CALLS") || replySegs.Length <= 1)) Message(command);
        }

        #endregion

        #region Static Classes

        public static string ResolveCallStatus(TCallStatus callStatus)
        {
            switch (callStatus)
            {
                case TCallStatus.clsBusy:
                    return "Busy";

                case TCallStatus.clsCancelled:
                    return "Cancelled";

                case TCallStatus.clsEarlyMedia:
                    return "Early Media";

                case TCallStatus.clsFailed:
                    return "Failed";

                case TCallStatus.clsFinished:
                    return "Finished";

                case TCallStatus.clsInProgress:
                    return "In Progress";

                case TCallStatus.clsLocalHold:
                    return "Local Hold";

                case TCallStatus.clsMissed:
                    return "Missed";

                case TCallStatus.clsOnHold:
                    return "On Hold";

                case TCallStatus.clsRefused:
                    return "Refused";

                case TCallStatus.clsRemoteHold:
                    return "Remote Hold";

                case TCallStatus.clsRinging:
                    return "Ringing";

                case TCallStatus.clsRouting:
                    return "Routing";

                case TCallStatus.clsTransferred:
                    return "Transferred";

                case TCallStatus.clsTransferring:
                    return "Transferring";

                case TCallStatus.clsUnknown:
                    return "Not Connected";

                case TCallStatus.clsUnplaced:
                    return "Unplaced";

                case TCallStatus.clsVoicemailBufferingGreeting:
                    return "Voicemail Buffering Greeting";

                case TCallStatus.clsVoicemailCancelled:
                    return "Voicemail Cancelled";

                case TCallStatus.clsVoicemailFailed:
                    return "Voicemail Failed";

                case TCallStatus.clsVoicemailPlayingGreeting:
                    return "Voicemail Playing Greeting";

                case TCallStatus.clsVoicemailRecording:
                    return "Voicemail Recording";

                case TCallStatus.clsVoicemailSent:
                    return "Voicemail Sent";

                case TCallStatus.clsVoicemailUploading:
                    return "Voicemail Uploading";

                default:
                    return "Unknown";


            }
        }
        public static bool IsCallOnHold(TCallStatus callStatus)
        {
            if (callStatus == TCallStatus.clsOnHold || callStatus == TCallStatus.clsLocalHold || callStatus == TCallStatus.clsRemoteHold)
                return true;
            else
                return false;
        }
        public static bool HasCallFailed(TCallStatus callStatus)
        {
            if (callStatus == TCallStatus.clsCancelled || callStatus == TCallStatus.clsFailed || callStatus == TCallStatus.clsBusy || callStatus == TCallStatus.clsMissed || callStatus == TCallStatus.clsRefused || callStatus == TCallStatus.clsUnknown || callStatus == TCallStatus.clsUnplaced)
                return true;
            else
                return false;
        }
        public static bool IsCallInProgress(TCallStatus callStatus)
        {
            if (callStatus == TCallStatus.clsInProgress)
                return true;
            else
                return false;
        }
        public static bool IsCallRinging(TCallStatus callStatus)
        {
            if (callStatus == TCallStatus.clsEarlyMedia || callStatus == TCallStatus.clsRinging || callStatus == TCallStatus.clsRouting || callStatus == TCallStatus.clsTransferring)
                return true;
            else
                return false;
        }
        public static bool HasCallFinished(TCallStatus callStatus)
        {
            if (callStatus == TCallStatus.clsFinished)
                return true;
            else
                return false;
        }
        public static bool IsAnswerMachine(TCallStatus callStatus)
        {
            if (callStatus == TCallStatus.clsVoicemailBufferingGreeting || callStatus == TCallStatus.clsVoicemailRecording || callStatus == TCallStatus.clsVoicemailPlayingGreeting || callStatus == TCallStatus.clsVoicemailUploading)
                return true;
            else
                return false;
        }

        #endregion
    }
}
